import Utils from "../common/Util";
import GameManager from "../common/GameManager";
import GameData from "../common/GameData";
import Block from "./Block";
import { getNextPowerOfTwo } from "../shape/math";
import HttpHelper from "../common/HttpHelper";
import Logger from "../Logger/Logger";

export enum BlockType {
    none,
    HT_2,
    HT_4,
    HT_8,
    HT_16,
    HT_32,
    HT_64,
    HT_128,
    HT_256,
    HT_512,
    HT_1024,
    HT_2048,   //以上都是普通方块
    HT_Universal,   //万能方块
    HT_Bomb,   //炸弹
    HT_Max,
}

export interface IBlock {
    pos: cc.Vec2,
    type: BlockType,
    index: number,
    neighbor: number[],
    node: cc.Node,
    dirty:Boolean,
    isProp: boolean
}

export var neigborConfig =
    [
        [-1, -1, 1, 5, 4, -1], //0
        [-1, -1, 2, 6, 5, 0], //1
        [-1, -1, 3, 7, 6, 1],//2
        [-1, -1, -1, 8, 7, 2],//3
        [-1, 0, 5, 10, 9, -1],//4
        [0, 1, 6, 11, 10, 4],//5
        [1, 2, 7, 12, 11, 5],//6
        [2, 3, 8, 13, 12, 6],//7
        [3, -1, -1, 14, 13, 7],//8
        [-1, 4, 10, 16, 15, -1],//9
        [4, 5, 11, 17, 16, 9],//10
        [5, 6, 12, 18, 17, 10],//11
        [6, 7, 13, 19, 18, 11],//12
        [7, 8, 14, 20, 19, 12],//13
        [8, -1, -1, 21, 20, 13],//14
        [-1, 9, 16, 22, -1, -1],//15
        [9, 10, 17, 23, 22, 15],//16
        [10, 11, 18, 24, 23, 16],//17
        [11, 12, 19, 25, 24, 17],//18
        [12, 13, 20, 26, 25, 18],//19
        [13, 14, 21, 27, 26, 19],//20
        [14, -1, -1, -1, 27, 20],//21
        [15, 16, 23, 28, -1, -1],//22
        [16, 17, 24, 29, 28, 22],//23
        [17, 18, 25, 30, 29, 23],//24
        [18, 19, 26, 31, 30, 24],//25
        [19, 20, 27, 32, 31, 25],//26
        [20, 21, -1, -1, 32, 26],//27
        [22, 23, 29, 33, -1, -1],//28
        [23, 24, 30, 34, 33, 28],//29
        [24, 25, 31, 35, 34, 29],//30
        [25, 26, 32, 36, 35, 30],//31
        [26, 27, -1, -1, 36, 31],//32
        [28, 29, 34, -1, -1, -1],//33
        [29, 30, 35, -1, -1, 33],//34
        [30, 31, 36, -1, -1, 34],//35
        [31, 32, -1, -1, -1, 35],//36
    ];
// export var chances = [
//     0,
//     100,
//     91,
//     82,
//     73,
//     64,
//     55,
//     46,
//     30,
//     18,
//     15
// ];

export var chances = [
    0,
    20,
    25,
    30,
    70,
    100,
    200,
    180,
    150,
    100,
    0
];

// export var chances = [
//     0,
//     100, // 2
//     91, //4
//     82,//8
//     73,//16
//     64,//32
//     55,//64
//     46,//128
//     30,//512
//     0,//1024
//     0
// ];


export var blockTypeVals = [
    1,
    2,
    4,
    8,
    16,
    32,
    64,
    128,
    256,
    512,
    1024,
    2048
]


export interface IDragData {
    posType: number, //0:只有一个 1: (-45,0),(45,0) 2:(-45,45),(45,-45)
    blockArr: number[]
}

export default class BlockManager extends cc.EventTarget {
    private revivalTimes = 2
    private _blockNumber = 37;
    private _maxBlockLevel: BlockType = BlockType.HT_2;
    private _dragData: IDragData = {
        posType: -1,
        blockArr: []
    };

    private static _instance = null;

    public blockArr: IBlock[] = [];
    public gridSpriteFrameArr: cc.SpriteFrame[] = [];
    public hintSpriteFrame: cc.SpriteFrame = null;

    public get dragData(): IDragData {
        return this._dragData;
    }

    public set2048DragData(): void {
        this._dragData = {
            posType: 0,
            blockArr: [BlockType.HT_2048, 0]
        }
    }

    public buildDragData(): void {
        // //随机生成方块
        let gridNum = Utils.RandomNum(1, 100) >= 65 ? 2 : 1;  //35% 是生成两个
        let canChosePosType: number[] = [];
        let canChoseBlockType: number[] = [];
        if (gridNum == 1) {
            canChosePosType.push(0);
        } else {
            if (this.checkHasHorizonSpace()) {
                canChosePosType.push(1);
            }
            if (this.checkCrossSpaceLeft()) {
                canChosePosType.push(2);
            }
            if (this.checkCrossSpaceRight()) {
                canChosePosType.push(3);
            }
        }

        // var availableColorsArray = this.getAvailableColorsArray(this._maxBlockLevel);
        var availableColorsArray = this.getAvailableColorsArray(this.getCurMaxLevel());
        var level1 = this.getRandomSkin(availableColorsArray, true);

        var level2 = 0;
        if (gridNum != 1) {
            level2 = this.getRandomSkin(availableColorsArray, true);
        }

        let ranPosType: number = Utils.RandomElement(canChosePosType);
        if (ranPosType == 0) {
            // level1 = this.checkMatchType();
        }
        this._dragData = {
            posType: ranPosType,
            blockArr: [level1, level2]
        }
        // this._dragData = {
        //     posType: 0,
        //     blockArr: [BlockType.HT_512]
        // }

        let gameData: GameData = GameManager.getInstance().gameData;
        gameData.SetCurPosType(this._dragData.posType);
        gameData.AddRandomHexagon(this._dragData.blockArr);
    }

    private getCurMaxLevel(): number {
        let maxLevel: number = BlockType.HT_2;
        for (let i = 0; i < this.blockArr.length; i++) {
            if (this.blockArr[i].type > maxLevel) {
                maxLevel = this.blockArr[i].type;
            }
        }
        return maxLevel;
    }

    private checkMatchType(): number {
        function search(arr: number[]) {
            arr = arr.filter(val => {
                return val != 0;
            })
            var maxCount = 0,
                maxItem = 1,
                obj = {}
            arr.forEach(function (item) {
                obj[item] ? (obj[item].count += 1) : obj[item] = { count: 1 }
                obj[item].count > maxCount && (maxCount = obj[item].count, maxItem = item)
            })
            //return {item:maxItem,count:maxCount}
            return maxItem;
        }

        let typeArr: number[] = [];
        for (let i = 0; i < this.blockArr.length; i++) {
            typeArr.push(this.blockArr[i].type);
        }
        return search(typeArr);
    }

    constructor() {
        super();
        this.initBlocks();
    }

    public static getInstance(): BlockManager {
        if (!BlockManager._instance) {
            BlockManager._instance = new BlockManager();
        }
        return BlockManager._instance;
    }

    private initBlocks() {
        for (let i = 0; i < this._blockNumber; i++) {
            let block: IBlock = {
                type: BlockType.none,
                pos: null,
                index: i,
                neighbor: [],
                node: null,
                dirty: false,
                isProp: false
            }
            this.blockArr.push(block);
        }
        this.initNeighbors();
    }

    public set maxBlockLevel(val: BlockType) {
        if (val > BlockType.HT_1024) {
            val = BlockType.HT_1024;
        }
        if (val < this._maxBlockLevel) {
            return;
        }
        this._maxBlockLevel = val;
    }


    private checkHasHorizonSpace(): boolean {
        for (let i = 0; i < this.blockArr.length; i++) {
            if (this.blockArr[i].type == BlockType.none) {
                let neighbor1: IBlock = this.blockArr[neigborConfig[i][2]];
                let neighbor2: IBlock = this.blockArr[neigborConfig[i][5]];
                if (neighbor1 && neighbor1.type == BlockType.none) {
                    return true;
                }
                if (neighbor2 && neighbor2.type == BlockType.none) {
                    return true;
                }
            }
        }
        return false;
    }

    private checkCrossSpaceLeft(): boolean {
        for (let i = 0; i < this.blockArr.length; i++) {
            if (this.blockArr[i].type == BlockType.none) {
                let neighbor1: IBlock = this.blockArr[neigborConfig[i][1]];
                let neighbor2: IBlock = this.blockArr[neigborConfig[i][4]];
                if (neighbor1 && neighbor1.type == BlockType.none) {
                    return true;
                }
                if (neighbor2 && neighbor2.type == BlockType.none) {
                    return true;
                }
            }
        }
        return false;
    }

    private checkCrossSpaceRight(): boolean {
        for (let i = 0; i < this.blockArr.length; i++) {
            if (this.blockArr[i].type == BlockType.none) {
                let neighbor1: IBlock = this.blockArr[neigborConfig[i][0]];
                let neighbor2: IBlock = this.blockArr[neigborConfig[i][3]];
                if (neighbor1 && neighbor1.type == BlockType.none) {
                    return true;
                }
                if (neighbor2 && neighbor2.type == BlockType.none) {
                    return true;
                }
            }
        }
        return false;
    }


    private checkSingle(): boolean {
        for (let i = 0; i < this.blockArr.length; i++) {
            if (this.blockArr[i].type == BlockType.none) {
                return true;
            }
        }
        return false;
    }

    /**
     * gameover
     */
    public checkGameOver(): boolean {
        return !this.checkSingle();
    }

    public getBlockByIndex(index: number): IBlock {
        if (index == -1) {
            return null;
        }
        return this.blockArr[index];
    }

    private initNeighbors() {

        for (let i = 0; i < this.blockArr.length; i++) {
            this.blockArr[i].neighbor = neigborConfig[i].filter((val) => {
                return val != -1;
            });
        }
    }

    public recalculateProbabilities(availableSkins) {
        var sumProbabilities = 0;
        availableSkins.forEach(function (value) { return sumProbabilities += chances[value.level]; });
        var prevProbability = 0;
        for (var i = 0; i < availableSkins.length; i++) {
            var currentProbability = prevProbability + chances[availableSkins[i].level] / sumProbabilities;
            prevProbability = currentProbability;
            availableSkins[i].probability = currentProbability;
        }
    }


    public getRandomSkin(availableSkins, remove) {
        if (remove === void 0) { remove = false; }

        var rndValue = Math.random();
        var i = 0;
        while (i < availableSkins.length) {
            if (rndValue <= availableSkins[i].probability) {
                if (remove) {
                    var chosenLevel = availableSkins.splice(i, 1)[0].level;
                    this.recalculateProbabilities(availableSkins);
                    return chosenLevel;
                }
                return availableSkins[i].level;
            }
            i++;
        }
        return 1;
    }

    public getAvailableColorsArray(maxCandyLevel) {
        var sumProbabilities = 0;
        for (var i = 1; i <= maxCandyLevel; i++) {
            sumProbabilities += chances[i];
        }
        var prevProbability = 0;
        var availableColors = [];
        for (var i = 1; i <= maxCandyLevel; i++) {
            var currentProbability = prevProbability + chances[i] / sumProbabilities;
            prevProbability = currentProbability;
            availableColors.push({
                level: i,
                probability: currentProbability
            });
        }
        return availableColors;
    }


    public getGridNodeByType(type: BlockType): cc.Node {
        let node: cc.Node = new cc.Node();
        node.anchorX = node.anchorY = 0.5;
        var sprite = node.addComponent(cc.Sprite);
        sprite.spriteFrame = this.gridSpriteFrameArr[type];
        return node;
    }

    public getHintNode(): cc.Node {
        let node: cc.Node = new cc.Node();
        node.anchorX = node.anchorY = 0.5;
        var sprite = node.addComponent(cc.Sprite);
        sprite.spriteFrame = this.hintSpriteFrame;
        return node;
    }

    
    public reset(): void {
        this._maxBlockLevel = BlockType.HT_2;
        this.resetRevivalTimes();
        for (let i = 0; i < this.blockArr.length; i++) {
            this.blockArr[i].type = BlockType.none;
        }
    }

    public hasRevivalTimes() {
        if(this.revivalTimes > 0) {
            this.revivalTimes--;
            return true;
        }
        return false;
    }

    private resetRevivalTimes() {
        this.revivalTimes = 2;
    }

    public revivalGame() {
        let one = Utils.RandomNum(0, 12);
        let two = Utils.RandomNum(12, 24);
        let three = Utils.RandomNum(24, 36);
        Logger.log("revivalGame, " + one + ";" + two + ";" + three);
        this.blockArr[one].isProp = true;
        this.blockArr[one].type = BlockType.HT_2048
        this.blockArr[two].isProp = true;
        this.blockArr[two].type = BlockType.HT_2048
        this.blockArr[three].isProp = true;
        this.blockArr[three].type = BlockType.HT_2048
    }

}